/**
 * @file iic_oled.c
 * @author WaterFairy (995637517@qq.com)
 * @brief
 * @version 1.0.0
 * @date 2022-06-24 21:06:79
 *
 * 0.96寸 OLED显示屏
 * 0x78 0x7A
 * 1.工作电压 3.3V/5V
 * 2.通讯接口 3-wire SPI , 4-wire I2C
 * 3.屏幕类型 OLED
 * 4.控制芯片 SSD1306
 * 5.分辨率   128*64
 * 6.外形尺寸 0.96寸
 * 7.显示颜色 ?
 * 8.工作温度 -20C ~ 70C
 * 9.存储温度 -30C ~ 80C
 * 10.视角 >
 *
 * 分为8页 每页宽128bit 高8bit
 *
 *
 *
 * @copyright Copyright (c) 2022
 *
 */
#include <stdint.h>
#include "iic_soft.h"
#include "stdarg.h"
#include <wiringPi.h>

 // SlaveAddress 0x78
#define OLED 0x78
#include "font_module.h"
/**
 * @brief 使用添加功能 字符拼接上次末尾
 *
 */
 //总page页数
#define TOTAL_PAGE 8
//列数
#define TOTAL_COLUMN 128
//当前页/列
uint8_t current_page = 0;
uint8_t current_column = 0;

/**
 * @brief 写指令
 *
 * @param cmd
 */
void iic_oled_write_cmd(uint8_t cmd)
{
    // 1.起始信号
    iic_soft_start();
    // 2.写地址
    iic_soft_write_byte(OLED);
    iic_soft_ack();
    // 3.写指令 Co(0)+D(1)/C(0)+000000
    //  0x00
    iic_soft_write_byte(0x00);
    iic_soft_ack();
    // 4.写命令内容
    iic_soft_write_byte(cmd);
    iic_soft_ack();
    // 5.结束
    iic_soft_stop();
}

/**
 * @brief 写数据
 *
 * @param data
 */
void iic_oled_write_data(uint8_t data)
{
    // 1.起始信号
    iic_soft_start();
    // 2.写地址
    iic_soft_write_byte(OLED);
    iic_soft_ack();
    // 3.写指令 Co(0)+D(1)/C(0)+000000
    //  0x00
    iic_soft_write_byte(0x40);
    iic_soft_ack();
    // 4.写命令内容
    iic_soft_write_byte(data);
    iic_soft_ack();
    // 5.结束
    iic_soft_stop();
}

/**
 * @brief 清屏
 *
 */
void iic_oled_clear(void)
{
    for (uint8_t i = 0; i < 8; i++)
    {
        //设置页 0xB0 = 1011 0000
        //->0xB0 ->0xB1 ->0xB2 ->0xB3 ...
        iic_oled_write_cmd(0xB0 | i); //页
        iic_oled_write_cmd(0x00);     //低位
        iic_oled_write_cmd(0x10);     //高位
        for (uint8_t j = 0; j < 128; j++)
        {
            iic_oled_write_data(0x00);
        }
    }
}

void iic_oled_on(void)
{
    iic_oled_write_cmd(0x8D); //充电泵设置
    iic_oled_write_cmd(0x24); //开启电荷泵
    iic_oled_write_cmd(0xAF); //显示开启
}

void iic_oled_off(void)
{
    iic_oled_write_cmd(0x8D); //充电泵设置
    iic_oled_write_cmd(0x24); //开启电荷泵
    iic_oled_write_cmd(0xAE); //显示关闭
}

void iic_oled_init(void)
{
    iic_soft_init();

    //延时200ms
    delayMicroseconds(200);

    iic_oled_write_cmd(0xA8); //设置分辨率
    iic_oled_write_cmd(0x3f); //设置128*64 0x3F ; 128*32 0x1F

    iic_oled_write_cmd(0xDA); //设置COM硬件引脚配置,适应分辨率
    iic_oled_write_cmd(0x12); // 0x12:0.96_128*64 ; 0x02:0.91_128*32
    iic_oled_write_cmd(0xA6); //设置显示方式 正常显示0xA6 , 反向显示0xA7

    iic_oled_write_cmd(0xD3); //设置显示偏移
    iic_oled_write_cmd(0x00); //默认设置00没有偏移
    iic_oled_write_cmd(0x40); //设置显示开始0-63
    iic_oled_write_cmd(0xA1); //段SEGMENT重映射对于IIC通讯四引脚OLED要设置
    iic_oled_write_cmd(0x81); //对比度设置指令
    iic_oled_write_cmd(0xFF); //亮度调节OXOO-OXFF数值越大亮度越大否则相反
    iic_oled_write_cmd(0xA4); //输出遵循RAM内容0X05输出忽略RAM内容
    iic_oled_write_cmd(0xA6); //设置显示方式正常显示（OXA6）正常显示:（OXA7）反向显示
    iic_oled_write_cmd(0xD5); //设置显示时钟分频/振荡器频率
    iic_oled_write_cmd(0xF0); //设置分辨率值
    iic_oled_write_cmd(0x8D); //充电泵设置
    iic_oled_write_cmd(0X14); // Ox14:允许在显示开启的时候使用,0x10:不允许在开启前使用
    iic_oled_write_cmd(0xAE); //显示关闭0xAF:显示开启; 0xAE:显示关闭
    iic_oled_write_cmd(0x20); //设置内存地址模式有三种模式水平/垂直/页寻址(默认)
    iic_oled_write_cmd(0x02); //水平0x00/垂直0x01/页寻址0x02(默认)
    iic_oled_write_cmd(0xB0); //为页寻址模式设置页面开启地址0-7
    iic_oled_write_cmd(0xC8); //设置COM扫描方式OXCO上下反置左到右/OXC8正常右到左
    iic_oled_write_cmd(0x00); //设置低列地址
    iic_oled_write_cmd(0x10); //设置高列地址
    iic_oled_write_cmd(0x40); //设置显示开始行0-63
    iic_oled_write_cmd(0XD9); //设置预充电时期
    iic_oled_write_cmd(0x22); //充电时间
    iic_oled_write_cmd(0xDB); //设置取消选择级别
    iic_oled_write_cmd(0X20); //默认0x20 0.77xvcc
    iic_oled_write_cmd(0xAF); //显示开启
    iic_oled_off();
    iic_oled_clear(); //清屏
    iic_oled_on();
}

/**
 * @brief
 *
 * @param page          页
 * @param page_size     页数
 * @param column        列
 * @param column_size   列数
 * @param data          数据
 */
void iic_oled_display(uint8_t page, uint8_t page_size, uint8_t column, uint8_t column_size, char* data)
{
    for (uint8_t i = 0; i < page_size; i++)
    {
        //设置页
        iic_oled_write_cmd((0xB0 | i) + page);
        //如果 20 = (0001 0100)
        //设置列 高位 0001 (0001) = 0x10 + (column >> 4 & 0x0F)
        //设置列 低位 0000 (0100) = column & 0x0F
        iic_oled_write_cmd(0x10 + (column >> 4 & 0x0F));
        iic_oled_write_cmd(column & 0x0F);

        for (uint8_t j = 0; j < column_size; j++)
        {
            iic_oled_write_data(*data++);
        }
    }
}

/**
 * @brief 显示字母
 *
 * @param page      页
 * @param column    列
 * @param letter    字母
 */
void iic_oled_display_font(uint8_t page, uint8_t column, char* data)
{
    while (*data != '\0')
    {
        //换行
        if (*data == '\n')
        {
            *data++;
            page += 2;
            column = 0;
            current_column = column;
            current_page = page;
        }
        else
        {
            //页是否用完
            if (page >= TOTAL_PAGE)
            {
                page = 0;
                column = 0;
                iic_oled_clear();
            }

            //列是否用完
            if (column >= TOTAL_COLUMN)
            {
                page += 2;
                column = 0;
            }

            //输入数据
            uint8_t index = *data++ - ' ';
            iic_oled_display(page, 2, column, 8, font_ackii[index]);
            column += 8;
            current_column = column;
            current_page = page;
        }
    }
}
/**
 * @brief oled打印
 *
 * @param format
 * @param ...
 */
void iic_oled_printf_position(uint8_t page, uint8_t column, const char* format, ...)
{
    va_list pArgs;
    va_start(pArgs, format);
    char str[100];
    vsprintf(str, format, pArgs);
    va_end(pArgs);
    iic_oled_display_font(page, column, str);
}
/**
 * @brief oled打印
 *
 * @param format
 * @param ...
 */
void iic_oled_printf(const char* format, ...)
{
    va_list pArgs;
    va_start(pArgs, format);
    char str[100];
    vsprintf(str, format, pArgs);
    va_end(pArgs);
    iic_oled_display_font(current_page, current_column, str);
}
